package com.binance.binancedemo.recycler;

import android.support.annotation.Nullable;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;


import com.binance.binancedemo.utils.L;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 */
public class BaseRecyclerAdapter<C extends BaseRecyclerCell, VH extends BaseRecyclerViewHolder> extends RecyclerView.Adapter {
    protected List<C> mCells = new ArrayList<>();
    protected AtomicBoolean mBeginChange = new AtomicBoolean(false);
    protected RecyclerView mRecyclerView;
    /**
     * 用于Diff判断
     */
    private List<C> mTempList = new ArrayList<>();

    public List<C> getCells() {
        return mCells;
    }

    public void setCells(List<C> mCells) {
        this.mCells.clear();
        if(mCells!=null){
            this.mCells.addAll(mCells);
        }
    }

    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
        mRecyclerView = (RecyclerView) parent;
        C cell = getItem(getFirstPositionByType(viewType));
        return cell == null ? null : (VH) cell.createViewHolder(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        C cell = getItem(position);
        if (cell != null) {
            cell.setAdapter(this);
            cell.setRecyclerView(mRecyclerView);
            cell.bindViewHolder((VH) holder);
        }
    }

    @Override
    public void onViewRecycled(RecyclerView.ViewHolder holder) {
        super.onViewRecycled(holder);
        if (holder instanceof BaseRecyclerViewHolder) {
            ((BaseRecyclerViewHolder) holder).onViewRecycled();
        }
    }

    @Override
    public int getItemCount() {
        return mCells == null ? 0 : mCells.size();
    }

    @Override
    public int getItemViewType(int position) {
        C cell = getItem(position);
        if (cell == null) {
            throw new IllegalArgumentException("position:" + position);
        }
        return cell.getType();
    }

    public int getFirstPositionByType(int viewType) {
        int size = getItemCount();
        for (int i = 0; i < size; i++) {
            if (getItem(i).getType() == viewType) {
                return i;
            }
        }
        return -1;
    }

    public C getItem(int position) {
        return (position >= 0 && position < getItemCount()) ? mCells.get(position) : null;
    }

    public void add(C item) {
        if (item == null) {
            return;
        }
        int count = getItemCount();
        mCells.add(item);
        if (!mBeginChange.get()) {
            notifyItemInserted(count == 0 ? 0 : count);
        }
    }

    public void add(int index, C item) {
        if (item == null) {
            return;
        }
        mCells.add(index, item);
        if (!mBeginChange.get()) {
            notifyItemInserted(index);
        }
    }

    public void addAll(Collection<? extends C> array) {
        addAll(getItemCount(), array);
    }

    public void addAll(int position, Collection<? extends C> array) {
        if (array == null) {
            return;
        }
        if (position < 0) {
            return;
        }
        mCells.addAll(position, array);
        if (!mBeginChange.get()) {
            notifyItemRangeInserted(position, array.size());
        }
    }

    public void set(List<C> list) {
        if (list == null || mCells == null) {
            return;
        }
        int oldEnd = mCells.size();
        int newEnd = list.size();
        mCells.clear();
        mCells.addAll(list);

        if (!mBeginChange.get()) {
            if (oldEnd == 0) {
                notifyItemRangeInserted(0, newEnd);
            } else if (oldEnd == newEnd) {
                notifyItemRangeChanged(0, newEnd);
            } else if (oldEnd < newEnd) {
                notifyItemRangeChanged(0, oldEnd);
                notifyItemRangeInserted(oldEnd, newEnd - oldEnd);
            } else {
                notifyItemRangeChanged(0, newEnd);
                notifyItemRangeRemoved(newEnd, oldEnd - newEnd);
            }
        }
    }

    /**
     * @param headerCount
     * @param list
     */
    public void smartSet(int headerCount, List<C> list, OnSmartSetFinishListener listener) {
        if (list == null || mCells == null) {
            return;
        }
        if (headerCount > 0) {
            List<C> headerList = new ArrayList<>(mCells.subList(0, headerCount));
            list.addAll(0, headerList);
        }
        beginChange();
        set(list);
        endChange(true);
        if (listener != null) {
            listener.onSmartSetFinish();
        }
    }

    public void onDestroyView() {
        for (int i = 0; i < mCells.size(); i++) {
            BaseRecyclerCell cell = mCells.get(i);
            cell.onDestroyView();
        }
        mRecyclerView = null;
    }

    public interface OnSmartSetFinishListener {
        public void onSmartSetFinish();
    }

    public void remove(C item) {
        int index = indexOf(item);
        remove(index);
    }

    public void remove(int i) {
        if (i < 0 || i >= getItemCount()) {
            return;
        }
        mCells.remove(i);
        if (!mBeginChange.get()) {
            notifyItemRemoved(i);
        }
    }

    /**
     * 移除元素，从start 到 end  ， start 和 end 都在范围内
     *
     * @param start
     * @param end
     */
    public void remove(int start, int end) {
        if (start > end) {
            L.e("start:" + start + " is bigger than end:" + end);
            return;
        }
        start = start < 0 ? 0 : start;
        int last = getItemCount() - 1;
        end = end > last ? last : end;
        for (int i = end; i >= start; i--) {
            mCells.remove(i);
        }
        if (!mBeginChange.get()) {
            notifyItemRangeRemoved(start, end - start + 1);
        }
    }

    public void clear() {
        int start = 0;
        int end = mCells.size();
        mCells.clear();
        if (!mBeginChange.get()) {
            notifyItemRangeRemoved(start, end);
        }
    }

    public int indexOf(C item) {
        return mCells == null ? -1 : mCells.indexOf(item);
    }

    public void beginChange() {
        mTempList = new ArrayList<>(mCells);
        mBeginChange.set(true);
    }

    public void endChange(boolean notifyChanged) {
        endChange(notifyChanged, true);
    }
    /**
     * 注意，加载更多的时候不要使用此函数，可能会导致加载完之后，列表没有维持原位而是滑到下面乱七八糟某个item处
     * @param notifyChanged
     */
    public void endChange(boolean notifyChanged, boolean detectMoves) {
        mBeginChange.set(false);
        if (!notifyChanged) {
            return;
        }
//        long s = System.currentTimeMillis();
        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
            @Override
            public int getOldListSize() {
                return mTempList.size();
            }

            @Override
            public int getNewListSize() {
                return mCells.size();
            }

            @Override
            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                BaseRecyclerCell oldCell = mTempList.get(oldItemPosition);
                BaseRecyclerCell newCell = mCells.get(newItemPosition);
                boolean areItemsTheSame = oldCell.areItemsTheSame(newCell);
                L.i("cost", "areItemsTheSame " + areItemsTheSame);
                return areItemsTheSame;
            }

            @Override
            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                C oldCell = mTempList.get(oldItemPosition);
                C newCell = mCells.get(newItemPosition);
                boolean contentSame = oldCell.areContentsTheSame(newCell);
                if(contentSame) {
                    mCells.set(newItemPosition, oldCell);
                }
                L.i("cost", "contentSame " + contentSame);
                return contentSame;
            }

            @Nullable
            @Override
            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
                BaseRecyclerCell oldCell = mTempList.get(oldItemPosition);
                BaseRecyclerCell newCell = mCells.get(newItemPosition);
                return oldCell.getChangePayload(newCell);
            }
        }, detectMoves);
//        long s1 = System.currentTimeMillis();
        diffResult.dispatchUpdatesTo(this);
//        long s2 = System.currentTimeMillis();
//        L.i("caldiff:" + (s1 - s) + "ms" + " dispatch:" + (s2 - s1) + "ms");
    }

    public RecyclerView getRecyclerView() {
        return mRecyclerView;
    }
}
